//==============================================================================
#include "ServerSocket.h"

using namespace net;
//==============================================================================
ServerSocket::ServerSocket(void)
{

}
//------------------------------------------------------------------------------
ServerSocket::ServerSocket(string port)
{
	setPort(port);
	socketConnection = INVALID_SOCKET;
	WSADATA wsaData;
    int startUpResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (startUpResult != 0 ||
		(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2))
    {
		Log::getInstance()->error("[SOCKET] WSAStartup() failed! ; (ErrorCode: " + Log::getInstance()->intToString(startUpResult) + " )");
    }
	obtainedAddress = NULL;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
	int addressResult = getaddrinfo(NULL, getPort().c_str(), &hints, &obtainedAddress);
    if (addressResult != 0)
    {
		Log::getInstance()->error("[SOCKET] getaddrinfo() failed! ; (ErrorCode: " + Log::getInstance()->intToString(addressResult) + " )");
    }
    socketConnection = socket(obtainedAddress->ai_family, obtainedAddress->ai_socktype, obtainedAddress->ai_protocol);
    if (socketConnection == INVALID_SOCKET)
    {
		Log::getInstance()->error("[SOCKET] socket() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
        freeaddrinfo(obtainedAddress);
    }
}
//------------------------------------------------------------------------------
ServerSocket::~ServerSocket(void)
{
	//WSACleanup();
}
//------------------------------------------------------------------------------
void ServerSocket::setPort(string port)
{
	this->port = port;
}
//------------------------------------------------------------------------------
string ServerSocket::getPort()
{
	return this->port;
}
//------------------------------------------------------------------------------
void ServerSocket::doBind(void) throw (NetworkException)
{
	if (socketConnection == INVALID_SOCKET)
	{
		Log::getInstance()->error("[SOCKET] Unable to bind on invalid socket.");
		throw NetworkException("");
	}
	sockaddr_in localAddress;
	localAddress.sin_family = AF_INET ;
	localAddress.sin_port = htons(atoi(port.c_str()));
	memset(&(localAddress.sin_zero), 0, 8);
	localAddress.sin_addr.s_addr = INADDR_ANY ;
	
	int bindResult = bind(socketConnection, (sockaddr*)&localAddress, sizeof(localAddress));
    if (bindResult == SOCKET_ERROR)
    {
		Log::getInstance()->error("[SOCKET] bind() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
        freeaddrinfo(obtainedAddress);
        throw NetworkException(WSAGetLastError());
    }
    freeaddrinfo(obtainedAddress);
}
//------------------------------------------------------------------------------
void ServerSocket::doListen(void) throw (NetworkException)
{
	int iResult = listen(socketConnection, SOMAXCONN);
    if (iResult == SOCKET_ERROR)
    {
		Log::getInstance()->error("[SOCKET] listen() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
        throw NetworkException(WSAGetLastError());
    }
}
//------------------------------------------------------------------------------
ClientSocket* ServerSocket::doAccept(void) throw (NetworkException)
{
	SOCKET socket = accept(socketConnection, NULL, NULL);
	if (socket == INVALID_SOCKET)
	{
		Log::getInstance()->error("[SOCKET] accept() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
		throw NetworkException(WSAGetLastError());
	}
	ClientSocket* clientSocket = new ClientSocket(socket);
	return clientSocket;
}
//------------------------------------------------------------------------------
void ServerSocket::setOption(OptionLevel ol, OptionName on) throw (NetworkException)
{
	int level = 0;
	int name = 0;
	BOOL TRUE_BOOLEAN_VALUE = TRUE;
    int BOOLEAN_OPTION_LENGTH = sizeof(BOOL);
	if (ol == TCP)
	{
		level = IPPROTO_TCP;
	}
	if (on == NO_DELAY)
	{
		name = TCP_NODELAY;
	}
	if (level > 0 && name > 0)
	{
		if (setsockopt(socketConnection, level, name, (char*)&TRUE_BOOLEAN_VALUE, BOOLEAN_OPTION_LENGTH) == SOCKET_ERROR)
		{
			string message("Unable to set option ");
			message.append(getSocketOptionReadable(on)).append(" for socket level ").append(getSocketLevelReadable(ol)).append(".");
			Log::getInstance()->error("[SOCKET] " + message);
			throw NetworkException(message);
		}
	}
}
//------------------------------------------------------------------------------
string ServerSocket::getSocketOptionReadable(int option)
{
	string readableOption = "";
	if (option == NO_DELAY)
	{
		readableOption = "TCP_NODELAY";
	}
	return readableOption;
}
//------------------------------------------------------------------------------
string ServerSocket::getSocketLevelReadable(int level)
{
	string readableLevel = "";
	if (level == TCP)
	{
		readableLevel = "IPPROTO_TCP";
	}
	return readableLevel;
}